home *** CD-ROM | disk | FTP | other *** search
- ;;; (C) Copyright 1990-1992 by Wade L. Hennessey. All rights reserved.
-
- (in-package "W")
-
- (defmacro letf ((accessor-form new-value) &body body)
- (let ((old-value (gensym "OLD-VALUE-")))
- `(let ((,old-value ,accessor-form))
- (unwind-protect (progn (setf ,accessor-form ,new-value)
- ,@body)
- (setf ,accessor-form ,old-value)))))
-
- (defmacro iterate (name var-vals &body body)
- `(labels ((,name ,(mapcar #'first var-vals)
- ,@body))
- (,name ,@(mapcar #'second var-vals))))
-
- (defmacro key-list-iterate (name (ivar list-form &optional done-form)
- var-init-pairs
- &body body)
- (let ((iteration-label
- (gensym (concatenate 'string (symbol-name name) "-")))
- (remaining-list (gensym "REMAINING-LIST-"))
- (vars (mapcar #'first var-init-pairs))
- (vals (mapcar #'second var-init-pairs)))
- `(macrolet ((,name (&key ,@(mapcar #'(lambda (var)
- `(,var ',var))
- vars))
- (list ',iteration-label
- (list 'cdr ',remaining-list)
- ,@vars)))
- (labels ((,iteration-label ,(cons remaining-list vars)
- (if (null ,remaining-list)
- ,done-form
- (let ((,ivar (car ,remaining-list)))
- ,@body))))
- (,iteration-label ,list-form ,@vals)))))
-
- ;;; Each name should be an object which may be coerced into
- ;;; a string. Return a symbol whose print-name is the concatenation
- ;;; of those strings.
- (defun names->symbol (&rest names)
- (intern (apply #'concatenate
- 'string
- (mapcar #'string names))))
-
- (defun tree-find (e tree)
- (labels ((loopy (rest)
- (if (atom rest)
- (if (null rest)
- nil
- (eq e rest))
- (or (loopy (car rest)) (loopy (cdr rest))))))
- (loopy tree)))
-
- (defun upto (e l)
- (nreverse (cdr (member e (reverse l)))))
-
- ;;; Return real body and decls
- (defun parse-body (body)
- (iterate separate ((rest (if (stringp (car body)) ; discard doc string
- (if (null (cdr body))
- body
- (cdr body))
- body))
- (decls nil))
- (let ((form (car rest)))
- (if (or (atom form)
- (not (eq (car form) 'declare)))
- (values rest ; real body
- decls)
- (separate (cdr rest) (append (cdr form) decls))))))
-
- ;;; Call INIT-FUNC N times, returing the results in a list.
- (defun n-list (n init-func)
- (if (= n 0)
- nil
- (cons (funcall init-func) (n-list (1- n) init-func))))
-
- ;;; Return every Nth element of L (for N >= 1). The odd
- ;;; part is that we always start with the first element.
- (defun every-n (n l)
- (iterate doit ((i 1)
- (rest l))
- (cond ((null rest) nil)
- ((= i 1) (cons (car rest) (doit n (cdr rest))))
- (t (doit (1- i) (cdr rest))))))
-
- (defun every-even (l)
- (every-n 2 l))
-
- (defun every-odd (l)
- (every-n 2 (cdr l)))
-
- (defun walk (func l)
- (if (atom l)
- (if (null l)
- nil
- (funcall func l))
- (or (walk func (car l)) (walk func (cdr l)))))
-
- (defun combos (objs)
- (iterate loopy ((rest objs)
- (combo nil))
- (if (null rest)
- (list (reverse combo))
- (loop for x in (car rest)
- nconcing (loopy (cdr rest) (cons x combo))))))
-
- ;;; INCREDIBLE! Common Lisp doesn't provide a standard function
- ;;; for printing the time of day out to a stream!
- ;;; I'm suprised there isn't a format directive to do this...
- (defun print-time (&key (stream t) (universal-time (get-universal-time))
- 24-hour-time)
- (multiple-value-bind (seconds
- minutes
- hours
- day
- month
- year
- day-of-week
- daylight-savings
- time-zone)
- (decode-universal-time universal-time)
- (declare (ignore daylight-savings time-zone))
- (let ((am? (< hours 12)))
- (format stream "~A:~2,'0D:~2,'0D ~Aon ~A, ~A ~A, ~A"
- (if 24-hour-time
- hours
- (let ((h (if am? hours (- hours 12))))
- (if (= h 0) 12 h)))
- minutes
- seconds
- (if 24-hour-time
- ""
- (if am? "am " "pm "))
- (svref #("Monday" "Tuesday" "Wednesday" "Thursday"
- "Friday" "Saturday" "Sunday")
- day-of-week)
- (svref #("January" "February" "March" "April" "May"
- "June" "July" "August" "September" "October"
- "November" "December")
- (1- month))
- day
- year))))
-
- ;;; This should probably be inline.
- (defun collect (func args)
- (do ((rest (cdr args) (cdr rest))
- (result (car args) (funcall func result (car rest))))
- ((null rest) result)))
-
- (defun same-length-p (l1 l2)
- (if (eq l1 '())
- (eq l2 '())
- (if (eq l2 '())
- nil
- (same-length-p (cdr l1) (cdr l2)))))
-
- ;;; CL macro defining stuff
-
- (defvar *macro-expanders* (make-hash-table :test #'eq))
-
- (defvar *compiler-macro-expanders* (make-hash-table :test #'eq))
-
- (defvar *type-macro-expanders* (make-hash-table))
-
- (defvar *macroexpand-hook-w* #'funcall
- "Function used to invoke macro expansion functions")
-
- (defstruct macro-env
- macros
- symbol-macros)
-
- (defstruct basic-macro
- original-arg-list
- expansion-function)
-
- (defstruct (macro (:include basic-macro)))
-
- (defstruct (compiler-macro (:include basic-macro)))
-
- (defstruct (type-macro (:include basic-macro)))
-
- (defmacro defmacro-w (name lambda-list &body body)
- `(define-macro ',name
- ,(parse-macro-definition name lambda-list nil body)))
-
- (defmacro deftype-w (name lambda-list &body body)
- `(define-type
- ',name
- ,(parse-macro-definition name lambda-list '* body)))
-
- (defmacro define-compiler-macro-w (name lambda-list &body body)
- `(define-compiler-macro-1 ',name
- ,(parse-macro-definition name lambda-list nil body)))
-
- (load "../cl/functions/cross-macros.lisp")
-
- ;;; ADD - make &body (body decls) destructure with PARSE-BODY
- (defun parse-macro-definition (name args optional-default body)
- (let ((args-without-&body (subst '&rest '&body args)))
- (multiple-value-bind (whole-arg args-without-whole)
- (if (eq (car args-without-&body) '&whole)
- (values (second args-without-&body) (cddr args-without-&body))
- (values (gensym "WHOLE") args-without-&body))
- (multiple-value-bind (env-arg args-without-macro-stuff)
- (let ((env (member '&environment args-without-whole :test #'eq)))
- (if (null env)
- (values (gensym "ENV") args-without-whole)
- (values (second env)
- (append (upto '&environment args-without-whole)
- (cddr env)))))
- (let ((dbind-list (if (null optional-default)
- args-without-macro-stuff
- (insert-optional-default
- args-without-macro-stuff
- `(quote ,optional-default)))))
- `(function (lambda (,whole-arg ,env-arg)
- (declare (ignoreable ,env-arg))
- (block ,name
- (destructuring-bind ,@(if (null dbind-list)
- '(nil nil)
- `(,dbind-list (cdr ,whole-arg)))
- (block ,name
- ,@body))))))))))
-
- ;;; TODO: Make it do nice error checking and reporting? Use
- ;;; it to replace the pattern matcher in some cases?
- ;;; DO NOT USE THIS???
- ;;; The expansion could be made more efficient (fewer cars/cdrs)
- ;;; if we factor out common subexpressions.
- (defmacro destructure ((vars form) &body body)
- (labels ((walk-vars (expr path)
- (if (atom expr)
- (if (null expr)
- expr
- `((,expr ,path)))
- (append (walk-vars (car expr) `(car ,path))
- (walk-vars (cdr expr) `(cdr ,path))))))
- (let ((f (gensym "FORM-")))
- `(let ((,f ,form))
- (let ,(walk-vars vars f) ,@body)))))
-
-
- ;;; HEY! I think key's should get the same treatment, but the
- ;;; manual doesn't think to say so....
- (defun insert-optional-default (lambda-list default)
- (loop for x in lambda-list
- for optional? = (or (and optional?
- (not (member x lambda-list-keywords
- :test #'eq)))
- (eq x '&optional))
- collect (if (and (not (eq x '&optional)) optional?)
- (typecase x
- (symbol `(,x ,default))
- (list `(,(first x) ,default ,@(cddr x))))
- x)))
-
- (defun macro-function-w (symbol)
- (let ((expander (lookup-macro-expander symbol *macro-expanders* nil)))
- (if (null expander)
- nil
- (basic-macro-expansion-function expander))))
-
- (defun compiler-macro-function-w (name &optional env)
- (declare (ignore env))
- (gethash name *compiler-macro-expanders*))
-
- (defun macro-arg-list (symbol table)
- (let ((expander (lookup-macro-expander symbol table nil)))
- (if (null expander)
- nil
- (basic-macro-original-arg-list expander))))
-
- (defun define-macro-function (symbol function arg-list table constructor)
- (setf (gethash symbol table)
- (funcall constructor
- :expansion-function function
- :original-arg-list arg-list))
- symbol)
-
- (defun macroexpand-w (form &optional local-macro-env)
- (expand-macro form *macro-expanders* local-macro-env t nil))
-
- (defun macroexpand-1-w (form &optional local-macro-env)
- (expand-macro form *macro-expanders* local-macro-env nil nil))
-
- (defun compiler-macroexpand-w (form &optional local-macro-env)
- (expand-macro form *compiler-macro-expanders* local-macro-env t nil))
-
- (defun compiler-macroexpand-1-w (form &optional local-macro-env)
- (expand-macro form *compiler-macro-expanders* local-macro-env nil nil))
-
- (defun expand-macro (form table menv
- repeat? original-call-is-a-macro?)
- (if (atom form)
- (let ((def (lookup-symbol-macro-def form menv)))
- (if (null def)
- (values form original-call-is-a-macro?)
- (values (second def) t)))
- (if (atom (car form))
- (let ((expander (lookup-macro-expander (car form)
- table
- menv)))
- (if (null expander)
- (values form original-call-is-a-macro?)
- (let ((exp (funcall *macroexpand-hook-w*
- (basic-macro-expansion-function expander)
- form
- menv)))
- (if (and repeat? (not (eq form exp)))
- (expand-macro exp table menv repeat? t)
- (values exp t)))))
- (values form original-call-is-a-macro?))))
-
- (defun lookup-macro-expander (name table menv)
- (let ((local (and (not (null menv))
- (assoc name (macro-env-macros menv) :test #'eq))))
- (if (null local)
- (gethash name table)
- (cdr local))))
-
- (defun lookup-symbol-macro-def (name menv)
- (and (not (null menv))
- (assoc name (macro-env-symbol-macros menv) :test #'eq)))
-
- (defun remove-macro-expander (name)
- (remhash name *macro-expanders*))
-
- (defun remove-compiler-macro-expander (name)
- (remhash name *compiler-macro-expanders*))
-
- (defun remove-type-macro-expander (name)
- (remhash name *type-macro-expanders*))
-
- (defun parse-in/out (spec)
- (multiple-value-bind (i o)
- (if (member '=> spec :test #'eq)
- (values (subseq spec 0 (position '=> spec))
- (subseq spec (1+ (position '=> spec))))
- (values spec nil))
- (values (mapcar #'first i)
- (mapcar #'first o)
- (mapcar #'second i)
- (mapcar #'second o))))
-
- (defun quoted-constant-p (l)
- (and (listp l)
- (eq (first l) 'quote)
- (null (cddr l))))
-
- (deftype lambda-expr ()
- '(satisfies lambda-expr?))
-
- ;;; Condition system thing.
- (defmacro with-keyword-pairs ((names expression &optional keywords-var)
- &body forms)
- (let ((temp (member '&rest names)))
- (unless (= (length temp) 2)
- (error "&REST keyword is ~:[missing~;misplaced~]." temp))
- (let ((key-vars (ldiff names temp))
- (key-var (or keywords-var (gensym)))
- (rest-var (cadr temp)))
- (let ((keywords (mapcar #'(lambda (x)
- (intern (string x)
- *keyword-package*))
- key-vars)))
- `(multiple-value-bind (,key-var ,rest-var)
- (parse-keyword-pairs ,expression ',keywords)
- (let ,(mapcar #'(lambda (var keyword)
- `(,var (getf ,key-var ,keyword)))
- key-vars keywords)
- ,@forms))))))
-